home

load libraries


### https://cran.r-project.org/web/packages/udpipe/vignettes/udpipe-usecase-postagging-lemmatisation.html
library(udpipe)
ud_model <- udpipe_download_model(language = "english")
library(tidyverse)
library(tidyr)
library(dplyr)
library(ggplot2)
library(ggrepel)
library(knitr)
library(tm)
library(quanteda)
library(lattice)
library(latticeExtra)
library(plotly)
library(pdp)
library(patchwork)

load the FW functions


### CODE DIRECTLY FROM: https://burtmonroe.github.io/TextAsDataCourse/Tutorials/TADA-FightinWords.nb.html#

fwgroups <- function(dtm, groups, pair = NULL, weights = rep(1,nrow(dtm)), k.prior = .1) {
  
  weights[is.na(weights)] <- 0
  
  weights <- weights/mean(weights)
  
  zero.doc <- rowSums(dtm)==0 | weights==0
  zero.term <- colSums(dtm[!zero.doc,])==0
  
  dtm.nz <- apply(dtm[!zero.doc,!zero.term],2,"*", weights[!zero.doc])
  
  g.prior <- tcrossprod(rowSums(dtm.nz),colSums(dtm.nz))/sum(dtm.nz)
  
  # 
  
  g.posterior <- as.matrix(dtm.nz + k.prior*g.prior)
  
  groups <- groups[!zero.doc]
  groups <- droplevels(groups)
  
  g.adtm <- as.matrix(aggregate(x=g.posterior,by=list(groups=groups),FUN=sum)[,-1])
  rownames(g.adtm) <- levels(groups)
  
  g.ladtm <- log(g.adtm)
  
  g.delta <- t(scale( t(scale(g.ladtm, center=T, scale=F)), center=T, scale=F))
  
  g.adtm_w <- -sweep(g.adtm,1,rowSums(g.adtm)) # terms not w spoken by k
  g.adtm_k <- -sweep(g.adtm,2,colSums(g.adtm)) # w spoken by groups other than k
  g.adtm_kw <- sum(g.adtm) - g.adtm_w - g.adtm_k - g.adtm # total terms not w or k 
  
  g.se <- sqrt(1/g.adtm + 1/g.adtm_w + 1/g.adtm_k + 1/g.adtm_kw)
  
  g.zeta <- g.delta/g.se
  
  g.counts <- as.matrix(aggregate(x=dtm.nz, by = list(groups=groups), FUN=sum)[,-1])
  
  if (!is.null(pair)) {
    pr.delta <- t(scale( t(scale(g.ladtm[pair,], center = T, scale =F)), center=T, scale=F))
    pr.adtm_w <- -sweep(g.adtm[pair,],1,rowSums(g.adtm[pair,]))
    pr.adtm_k <- -sweep(g.adtm[pair,],2,colSums(g.adtm[pair,])) # w spoken by groups other than k
    pr.adtm_kw <- sum(g.adtm[pair,]) - pr.adtm_w - pr.adtm_k - g.adtm[pair,] # total terms not w or k
    pr.se <- sqrt(1/g.adtm[pair,] + 1/pr.adtm_w + 1/pr.adtm_k + 1/pr.adtm_kw)
    pr.zeta <- pr.delta/pr.se
    
    return(list(zeta=pr.zeta[1,], delta=pr.delta[1,],se=pr.se[1,], counts = colSums(dtm.nz), acounts = colSums(g.adtm)))
  } else {
    return(list(zeta=g.zeta,delta=g.delta,se=g.se,counts=g.counts,acounts=g.adtm))
  }
}

############## FIGHTIN' WORDS PLOTTING FUNCTION

# helper function
makeTransparent<-function(someColor, alpha=100)
{
  newColor<-col2rgb(someColor)
  apply(newColor, 2, function(curcoldata){rgb(red=curcoldata[1], green=curcoldata[2],
                                              blue=curcoldata[3],alpha=alpha, maxColorValue=255)})
}

fw.ggplot.groups <- function(fw.ch, groups.use = as.factor(rownames(fw.ch$zeta)), max.words = 50, max.countrank = 400, colorpalette=rep("black",length(groups.use)), sizescale=2, title="Comparison of Terms by Groups", subtitle = "", caption = "Group-specific terms are ordered by Fightin' Words statistic (Monroe, et al. 2008)") {
  if (is.null(dim(fw.ch$zeta))) {## two-group fw object consists of vectors, not matrices
    zetarankmat <- cbind(rank(-fw.ch$zeta),rank(fw.ch$zeta))
    colnames(zetarankmat) <- groups.use
    countrank <- rank(-(fw.ch$counts))
  } else {
    zetarankmat <- apply(-fw.ch$zeta[groups.use,],1,rank)
    countrank <- rank(-colSums(fw.ch$counts))
  }
  wideplotmat <- as_tibble(cbind(zetarankmat,countrank=countrank))
  wideplotmat$term=names(countrank)
  #rankplot <- gather(wideplotmat, party, zetarank, 1:ncol(zetarankmat))
  rankplot <- gather(wideplotmat, groups.use, zetarank, 1:ncol(zetarankmat))
  rankplot$plotsize <- sizescale*(50/(rankplot$zetarank))^(1/4)
  rankplot <- rankplot[rankplot$zetarank < max.words + 1 & rankplot$countrank<max.countrank+1,]
  rankplot$groups.use <- factor(rankplot$groups.use,levels=groups.use)
  
  p <- ggplot(rankplot, aes((nrow(rankplot)-countrank)^1, -(zetarank^1), colour=groups.use)) + 
    geom_point(show.legend=F,size=sizescale/2) + 
    theme_classic() +
    theme(axis.ticks=element_blank(), axis.text=element_blank() ) +
    ylim(-max.words,40) +
    facet_grid(groups.use ~ .) +
    geom_text_repel(aes(label = term), size = rankplot$plotsize, point.padding=.05,
                    box.padding = unit(0.20, "lines"), show.legend=F, max.overlaps = Inf) +
    scale_colour_manual(values = alpha(colorpalette, .7)) + 
#    labs(x="Terms used more frequently overall →", y="Terms used more frequently by group →",  title=title, subtitle=subtitle , caption = caption) 
    labs(x=paste("Terms used more frequently overall -->"), y=paste("Terms used more frequently by group -->"),  title=title, subtitle=subtitle , caption = caption) 
  
}

options(ggrepel.max.overlaps = Inf)

fw.keys <- function(fw.ch,n.keys=10) {
  n.groups <- nrow(fw.ch$zeta)
  keys <- matrix("",n.keys,n.groups)
  colnames(keys) <- rownames(fw.ch$zeta)
  
  for (g in 1:n.groups) {
    keys[,g] <- names(sort(fw.ch$zeta[g,],dec=T)[1:n.keys])
  }
  keys
}

Compare Associated Press 1994-2010: Before and After “extremist” and other query terms

Query search: (activi* | ahbash | akromiya | anjem | ansharut | anticapital* | antidemocr* | antiestablish* | antifa | antigovern* | antimilitar* | antimonarch* | antipatri* | antireli* | antisem* | antisocia* | antisyst* | apost* | atharis | athei* | atheists | außerparlamentari* | authoritar* | bagau* | bigots | bplf | bukhari* | capitulatio* | conspirato* | counterj* | cybercalip* | damigo | dawro* | demon* | deradicaliza* | deviatio* | diqqi | dissid* | djamaat | dotbus* | ecofas* | espou* | ethnonationa* | extrem* | facists | fadaia* | fanat* | fasci* | fetö | fightdem* | freedo* | fundamental* | fuqra | gafatar | gamerga* | gemidzii | ghuluww | globali* | gramsc* | gülen* | hacktiv* | haquna | hardline | harkatul | hatemon* | heimwe* | hezbol* | hinduph* | hindutva | hizbut | hojja* | ideolog* | incitem* | inciters | insurr* | intacti* | islam4uk | islam* | jaljalat | jbakc | jihadi* | jmjb | jrtn | judai* | juhayman | jundu* | kadiza* | kahanism | kahanist | karram* | kaysa* | khalis* | khatmia | khawarij | khomein* | koutla | leftist | leftists | leftwing | liberatio* | madkha* | madkhal* | maimonid* | manosp* | mauras* | mcln | militan* | millatu | monarc* | mudja* | muhaji* | mujahid* | murab* | muttahi* | najjadah | nationali* | neofas* | neona* | opantish | oppositi* | paleolibertar* | paramili* | parliamenta* | pegida | populist | principa* | profe* | prowar | putinist | qadari | quranism | quranist | qutbism | qutbist | qutbists | racis* | radica* | reactioni* | reformis* | reichsbürgerbewe* | rightist | rightw* | rofiq | russoph* | sabireen | sadda* | salaf* | sayaff | scriptura* | secula* | separationi* | sharia4hol* | sikrikim | split* | squadism | strasse* | subver* | suidlan* | sukarn* | suprema* | supremac* | sympathi* | table* | tabliq | takfir | takfir* | takfi* |terror* | theoc* | titoite | triba* | trots* | trotsk* | ukrainoph* | ultraconserva* | ultralib* | ultranationa* | ultrar* | uscmo | wahabbi | wahab* | wahha* | xenoph* | yulde* | zinovie*)

Load and clean the data

text_cleaner<-function(corpus){
  tempcorpus<-Corpus(VectorSource(corpus))
  tempcorpus<-tm_map(tempcorpus,
                    removePunctuation)
  tempcorpus<-tm_map(tempcorpus,
                    stripWhitespace)
  tempcorpus<-tm_map(tempcorpus,
                    removeNumbers)
  tempcorpus<-tm_map(tempcorpus,
                     removeWords, stopwords("english"))
  tempcorpus<-tm_map(tempcorpus, 
                    stemDocument)
  return(tempcorpus)
}

Calculate FW.


e <- dfm(extremecorpus$content)
message(dim(e))
head(e)
Document-feature matrix of: 6 documents, 14,891 features (100.0% sparse).
       features
docs    us struggl uptick american plot attack past month last week
  text1  1       1      1        1    1      0    0     0    0    0
  text2  0       0      0        0    0      1    1     1    1    1
  text3  0       0      0        0    0      0    0     0    0    0
  text4  0       0      0        0    0      0    0     0    0    0
  text5  0       0      0        0    0      0    0     0    0    0
  text6  0       0      0        0    0      0    0     0    0    0
[ reached max_nfeat ... 14,881 more features ]
#############################################

e <- dfm_select(e, pattern = stopwords("english"), selection = "remove")
e <- dfm_select(e, min_nchar = 2)
e <- dfm_trim(e, min_termfreq = 4, min_docfreq = .05, verbose=TRUE)

#dim(e)
# sparsity(e)

#############################################

extrem_dtm <- convert(e, to='data.frame')
extrem_dtm <- extrem_dtm[-c(1)]
w <- which( sapply(extrem_dtm, class ) == 'character' )

#############################################

fw.extrem <- fwgroups(extrem_dtm, groups=extrem_NYT.dfm.long$Context)

Get and show the top words per group by zeta.

Context.before Context.after
islam group
palestinian hussein
war attack
iraqi parti
suspect elect
crack leader
jemaah jihad
crackdown milit
main movement
alleg said
support guerrilla
evid network
albanian regim
prodemocraci armi
sept politician
muslim organ
megawati outsid
battl univers
prevent bomb
kosovo rocket

Plot: Before in Blue, After in Red

Calculate by query item/search term


extrem_dtm_topn <- convert(e, to='data.frame')
extrem_dtm_topn$Number.of.hit <- extrem_NYT.dfm.long$Number.of.hit
extrem_dtm_topn$Context <- extrem_NYT.dfm.long$Context

topn_terms <- extrem_NYT.dfm.long %>%
      filter(Query.item %in% top_n$term)

extrem_dtm_topn_keep <- extrem_dtm_topn %>% 
    filter(Number.of.hit %in% topn_terms$Number.of.hit)

extrem_dtm_topn_keep_before <- extrem_dtm_topn_keep[grep("before",extrem_dtm_topn_keep$Context),]
extrem_dtm_topn_keep_after <- extrem_dtm_topn_keep[grep("after", extrem_dtm_topn_keep$Context),]

rr <-dim(topn_terms)[1]
r <- sum(length(extrem_dtm_topn_keep_before))
topn_terms_before <- topn_terms[seq(1,rr,2),]
topn_terms_after <- topn_terms[seq(2,rr,2),]

extrem_dtm_topn_keep_before <- extrem_dtm_topn_keep_before[-c(1, r-1, r)]
extrem_dtm_topn_keep_after <- extrem_dtm_topn_keep_after[-c(1, r-1, r)]

rm(rr)
rm(r)
rm(extrem_dtm_topn)

#############################################
fw.query_item_before <- fwgroups(extrem_dtm_topn_keep_before,groups = topn_terms_before$Query.item)
fwkeys.query_item_before <- fw.keys(fw.query_item_before, n.keys=15)
kable(fwkeys.query_item_before, caption = "Top 15 Words for Query Term: BEFORE")
Top 15 Words for Query Term: BEFORE
Islamic militants opposition Saddam terrorist
milit islam main iraqi sept
organ palestinian elect presid alqaida
strict kill polit iraq bin
radic suspect parliament war laden
somalia attack opposit saddam involv
hama taliban parti oust unit
secular isra minist baghdad suspect
hardlin muslim vote fall link
iran gaza democrat bush state
turkey armi prime hussein consid
extremist troop strong captur connect
malaysia israel percent regim appar
suprem pakistan despit usl osama
gaza forc poll former charg
fundamentalist arafat voic death list

p.fw.query_item_before <- fw.ggplot.groups(fw.query_item_before,sizescale=2,max.words=150,max.countrank=400,
                                           colorpalette = c('blue','blue','blue', 'blue','blue'),
                                           title = 'Comparison of Terms by Overall Top Terms: BEFORE')
p.fw.query_item_before


#############################################
fw.query_item_after <- fwgroups(extrem_dtm_topn_keep_after,groups = topn_terms_after$Query.item)
fwkeys.query_item_after <- fw.keys(fw.query_item_after, n.keys=15)
kable(fwkeys.query_item_after, caption = "Top 15 Words for Query Term: AFTER")
Top 15 Words for Query Term: AFTER
Islamic militants opposition Saddam terrorist
milit isra parti hussein attack
group israel leader regim organ
jihad kashmir democrat iraq state
hama gaza vote iraqi unit
movement palestinian parliament un activ
law kill lawmak weapon group
insurg attack protest saddam act
extremist region elect son network
front southern candid captur bomb
revolut fire politician kuwait suspect
republ rocket coalit mass threat
court pakistani social baghdad financ
fundamentalist fight conserv us sept
news border win resolut link
agenc area opposit palac cell

p.fw.query_item_after <- fw.ggplot.groups(fw.query_item_after,sizescale=2,max.words=150,max.countrank=400,
                                           colorpalette = c('red', 'red','red','red','red'),
                                          title = 'Comparison of Terms by Overall Top Terms: AFTER')
p.fw.query_item_after

NA
NA

Calculate Parts of speech by before and after

Calculate FW and keys


ud_model <- udpipe_load_model(ud_model$file_model)

txt <-as.character(extrem_NYT.dfm.long$context.text)

x_udp <- udpipe_annotate(ud_model, x = txt, doc_id = seq_along(txt))
x <- as.data.frame(x_udp)

x$doc_id <-as.integer(x$doc_id)

x_odd.before <- x[x$doc_id %% 2 == 1,]
x_even.after <-x[x$doc_id %% 2 == 0, ]

A few barchart functions


## UNIVERSAL PoS
UPOS_barchart <- function(df1, df2){
  stats1 <- txt_freq(df1$upos)
  stats1$key <- factor(stats1$key, levels = rev(stats1$key))
  
  stats2 <- txt_freq(df2$upos)
  stats2$key <- factor(stats2$key, levels = rev(stats2$key))
  
  c(barchart(key ~ freq, data = stats1, col = "cadetblue", 
        main = "UPOS (Universal Parts of Speech)\n frequency of occurrence: BEFORE vs AFTER", 
         xlab = "Freq"), 
    barchart(key ~ freq, data = stats2, col =  'skyblue',
         xlab = "Freq"))
}



## NOUNS
NOUNS_barchart <- function(df1, df2){
  
  stats1 <- subset(df1, upos %in% c("NOUN")) 
  stats1 <- txt_freq(stats1$token)
  stats1$key <- factor(stats1$key, levels = rev(stats1$key))
  
  stats2 <- subset(df2, upos %in% c("NOUN")) 
  stats2 <- txt_freq(stats2$token)
  stats2$key <- factor(stats2$key, levels = rev(stats2$key))
  
  c(barchart(key ~ freq, data = head(stats1, 20), col = "cadetblue", 
           main = "Most occurring nouns: BEFORE vs AFTER", xlab = "Freq"),
      barchart(key ~ freq, data = head(stats2, 20), col = "skyblue", 
            xlab = "Freq"))
}

## ADJECTIVES
ADJ_barchart <- function(df1, df2){
  
  stats1 <- subset(df1, upos %in% c("ADJ")) 
  stats1 <- txt_freq(stats1$token)
  stats1$key <- factor(stats1$key, levels = rev(stats1$key))
  
  stats2 <- subset(df2, upos %in% c("ADJ")) 
  stats2 <- txt_freq(stats2$token)
  stats2$key <- factor(stats2$key, levels = rev(stats2$key))
  
  c(barchart(key ~ freq, data = head(stats1, 20), col = "cadetblue", 
           main = "Most occurring adjectives: BEFORE vs AFTER", xlab = "Freq"),
      barchart(key ~ freq, data = head(stats2, 20), col = "skyblue", 
         xlab = "Freq"))
}

## Using RAKE to find keywords
RAKE_KW_barchart <- function(df1,df2){
  
  stats1 <- keywords_rake(x = df1, term = "lemma", group = "doc_id", 
                         relevant = df1$upos %in% c("NOUN", "ADJ"))
  stats1$key <- factor(stats1$keyword, levels = rev(stats1$keyword))
  
  stats2 <- keywords_rake(x = df2, term = "lemma", group = "doc_id", 
                         relevant = df2$upos %in% c("NOUN", "ADJ"))
  stats2$key <- factor(stats2$keyword, levels = rev(stats2$keyword))
  
  
  c(barchart(key ~ rake, data = head(subset(stats1, freq > 3), 20), col = "cadetblue", 
           main = "Keywords identified by RAKE: BEFORE vs AFTER", 
           xlab = "Rake"),
    barchart(key ~ rake, data = head(subset(stats2, freq > 3), 20), col = "skyblue", 
           xlab = "Rake"))
}

## Using Pointwise Mutual Information Collocations
PWI_barchart <- function(df1, df2){
  
  df1$word <- tolower(df1$token)
  stats1 <- keywords_collocation(x = df1, term = "word", group = "doc_id")
  stats1$key <- factor(stats1$keyword, levels = rev(stats1$keyword))
  
  df2$word <- tolower(df2$token)
  stats2 <- keywords_collocation(x = df2, term = "word", group = "doc_id")
  stats2$key <- factor(stats2$keyword, levels = rev(stats2$keyword))
  
  c(barchart(key ~ pmi, data = head(subset(stats1, freq > 3), 20), col = "cadetblue", 
           main = "Keywords identified by PMI Collocation: BEFORE vs AFTER", 
           xlab = "PMI (Pointwise Mutual Information)"),
      barchart(key ~ pmi, data = head(subset(stats2, freq > 3), 20), col = "skyblue", 
           xlab = "PMI (Pointwise Mutual Information)"))
}

## Using a sequence of POS tags (noun phrases / verb phrases)
POS_barchart <- function(df1, df2){
  
  df1$phrase_tag <- as_phrasemachine(df1$upos, type = "upos")
  stats1 <- keywords_phrases(x = df1$phrase_tag, term = tolower(df1$token), 
                            pattern = "(A|N)*N(P+D*(A|N)*N)*", 
                            is_regex = TRUE, detailed = FALSE)
  stats1 <- subset(stats1, ngram > 1 & freq > 3)
  stats1$key <- factor(stats1$keyword, levels = rev(stats1$keyword))
  
  df2$phrase_tag <- as_phrasemachine(df2$upos, type = "upos")
  stats2 <- keywords_phrases(x = df2$phrase_tag, term = tolower(df2$token), 
                            pattern = "(A|N)*N(P+D*(A|N)*N)*", 
                            is_regex = TRUE, detailed = FALSE)
  stats2 <- subset(stats2, ngram > 1 & freq > 3)
  stats2$key <- factor(stats2$keyword, levels = rev(stats2$keyword))
  
  c(barchart(key ~ freq, data = head(stats1, 20), col = "cadetblue", 
           main = "Keywords - simple noun phrases: BEFORE vs AFTER", xlab = "Frequency"),
      barchart(key ~ freq, data = head(stats2, 20), col = "skyblue", 
               xlab = "Frequency"))
}

Bar Charts from Functions Above


UPOS_barchart(x_odd.before, x_even.after)

NOUNS_barchart(x_odd.before, x_even.after)

ADJ_barchart(x_odd.before, x_even.after)

RAKE_KW_barchart(x_odd.before, x_even.after)

PWI_barchart(x_odd.before, x_even.after)

POS_barchart(x_odd.before, x_even.after)

Cooccurences


CO_OC_noun_adj_same_sent.before <- function(df1){
  
  library(igraph)
  library(ggraph)
  library(ggplot2)
  
  cooc <- cooccurrence(x = subset(df1, upos %in% c("NOUN", "ADJ")), 
                       term = "lemma", 
                       group = c("doc_id", "paragraph_id", "sentence_id"))

  wordnetwork <- head(cooc, 60)
  wordnetwork <- graph_from_data_frame(wordnetwork)
  
  ggraph(wordnetwork, layout = "fr") +
    geom_edge_link(aes(width = cooc, edge_alpha = cooc), edge_colour = "pink") +
    geom_node_text(aes(label = name), col = "darkgreen", size = 4) +
    theme_graph(base_family = "Arial Narrow") +
    theme(legend.position = "none") +
    labs(title = "Cooccurrences within sentence: BEFORE", subtitle = "Nouns & Adjective")
  
}

CO_OC_noun_adj_same_sent.after <- function(df2){
  
  library(igraph)
  library(ggraph)
  library(ggplot2)
  
  cooc <- cooccurrence(x = subset(df2, upos %in% c("NOUN", "ADJ")), 
                       term = "lemma", 
                       group = c("doc_id", "paragraph_id", "sentence_id"))

  wordnetwork <- head(cooc, 60)
  wordnetwork <- graph_from_data_frame(wordnetwork)
  
  ggraph(wordnetwork, layout = "fr") +
    geom_edge_link(aes(width = cooc, edge_alpha = cooc), edge_colour = "lightgreen") +
    geom_node_text(aes(label = name), col = "darkblue", size = 4) +
    theme_graph(base_family = "Arial Narrow") +
    theme(legend.position = "none") +
    labs(title = "Cooccurrences within sentence: AFTER", subtitle = "Nouns & Adjective")
  
}


CO_OC_noun_adj_same_sent.before(x_odd.before)

Attaching package: ‘igraph’

The following object is masked from ‘package:plotly’:

    groups

The following object is masked from ‘package:quanteda’:

    as.igraph

The following objects are masked from ‘package:dplyr’:

    as_data_frame, groups, union

The following objects are masked from ‘package:purrr’:

    compose, simplify

The following object is masked from ‘package:tidyr’:

    crossing

The following object is masked from ‘package:tibble’:

    as_data_frame

The following objects are masked from ‘package:stats’:

    decompose, spectrum

The following object is masked from ‘package:base’:

    union

CO_OC_noun_adj_same_sent.after(x_even.after)



##########################################

CO_OC_noun_adj_following.before <- function(df){
  cooc <- cooccurrence(df$lemma, relevant = df$upos %in% c("NOUN", "ADJ"), skipgram = 1)
  head(cooc)
  
  wordnetwork <- head(cooc, 60)
  wordnetwork <- graph_from_data_frame(wordnetwork)
  ggraph(wordnetwork, layout = "fr") +
    geom_edge_link(aes(width = cooc, edge_alpha = cooc), edge_colour = "lightgreen") +
    geom_node_text(aes(label = name), col = "darkgreen", size = 4) +
    theme_graph(base_family = "Arial Narrow") +
    labs(title = "Words following one another: BEFORE", subtitle = "Nouns & Adjective")
}

CO_OC_noun_adj_following.after <- function(df){
  cooc <- cooccurrence(df$lemma, relevant = df$upos %in% c("NOUN", "ADJ"), skipgram = 1)
  head(cooc)
  
  wordnetwork <- head(cooc, 60)
  wordnetwork <- graph_from_data_frame(wordnetwork)
  ggraph(wordnetwork, layout = "fr") +
    geom_edge_link(aes(width = cooc, edge_alpha = cooc), edge_colour = "skyblue") +
    geom_node_text(aes(label = name), col = "darkblue", size = 4) +
    theme_graph(base_family = "Arial Narrow") +
    labs(title = "Words following one another: AFTER", subtitle = "Nouns & Adjective")
}


CO_OC_noun_adj_following.before(x_odd.before)

CO_OC_noun_adj_following.after(x_even.after)

Cooccurences (part 2)


Corrs <- function(df){
  df$id <- unique_identifier(df, fields = c("sentence_id", "doc_id"))
  dtm <- subset(df, upos %in% c("NOUN", "ADJ"))
  dtm <- document_term_frequencies(dtm, document = "id", term = "lemma")
  dtm <- document_term_matrix(dtm)
  dtm <- dtm_remove_lowfreq(dtm, minfreq = 5)
  termcorrelations <- dtm_cor(dtm)
  y <- as_cooccurrence(termcorrelations)
  y <- subset(y, term1 < term2 & abs(cooc) > 0.2)
  y <- y[order(abs(y$cooc), decreasing = TRUE), ]
  print(y[1:25,])
}


Corrs(x_odd.before)
Corrs(x_even.after)

Corrs(x_odd.before)

Corrs(x_even.after)

home

LS0tCnRpdGxlOiAiRXh0cmVtKGlzdCArKSBGaWdodGluJyBXb3JkcyIKYXV0aG9yOiAiQnJlYW5uYSBFLiBHcmVlbiIKc3VidGl0bGU6IHByYWN0aWNpbmcKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgaHRtbF9ub3RlYm9vazoKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICB0aGVtZTogdW5pdGVkCiAgICB0b2M6IHllcwotLS0KCltob21lXShodHRwczovL2JyZWdyZWVuLmdpdGh1Yi5pby8pCgojIyBsb2FkIGxpYnJhcmllcwoKYGBge3IsIHJlc3VsdHM9J2hpZGUnfQoKIyMjIGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy91ZHBpcGUvdmlnbmV0dGVzL3VkcGlwZS11c2VjYXNlLXBvc3RhZ2dpbmctbGVtbWF0aXNhdGlvbi5odG1sCmxpYnJhcnkodWRwaXBlKQp1ZF9tb2RlbCA8LSB1ZHBpcGVfZG93bmxvYWRfbW9kZWwobGFuZ3VhZ2UgPSAiZW5nbGlzaCIpCgpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeSh0aWR5cikKbGlicmFyeShkcGx5cikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGdncmVwZWwpCmxpYnJhcnkoa25pdHIpCmxpYnJhcnkodG0pCmxpYnJhcnkocXVhbnRlZGEpCmxpYnJhcnkobGF0dGljZSkKbGlicmFyeShsYXR0aWNlRXh0cmEpCmxpYnJhcnkocGxvdGx5KQpsaWJyYXJ5KHBkcCkKbGlicmFyeShwYXRjaHdvcmspCgpgYGAKCiMjIGxvYWQgdGhlIEZXIGZ1bmN0aW9ucwoKYGBge3IgbG9hZF9md19mdW5jdGlvbnN9CgojIyMgQ09ERSBESVJFQ1RMWSBGUk9NOiBodHRwczovL2J1cnRtb25yb2UuZ2l0aHViLmlvL1RleHRBc0RhdGFDb3Vyc2UvVHV0b3JpYWxzL1RBREEtRmlnaHRpbldvcmRzLm5iLmh0bWwjCgpmd2dyb3VwcyA8LSBmdW5jdGlvbihkdG0sIGdyb3VwcywgcGFpciA9IE5VTEwsIHdlaWdodHMgPSByZXAoMSxucm93KGR0bSkpLCBrLnByaW9yID0gLjEpIHsKICAKICB3ZWlnaHRzW2lzLm5hKHdlaWdodHMpXSA8LSAwCiAgCiAgd2VpZ2h0cyA8LSB3ZWlnaHRzL21lYW4od2VpZ2h0cykKICAKICB6ZXJvLmRvYyA8LSByb3dTdW1zKGR0bSk9PTAgfCB3ZWlnaHRzPT0wCiAgemVyby50ZXJtIDwtIGNvbFN1bXMoZHRtWyF6ZXJvLmRvYyxdKT09MAogIAogIGR0bS5ueiA8LSBhcHBseShkdG1bIXplcm8uZG9jLCF6ZXJvLnRlcm1dLDIsIioiLCB3ZWlnaHRzWyF6ZXJvLmRvY10pCiAgCiAgZy5wcmlvciA8LSB0Y3Jvc3Nwcm9kKHJvd1N1bXMoZHRtLm56KSxjb2xTdW1zKGR0bS5ueikpL3N1bShkdG0ubnopCiAgCiAgIyAKICAKICBnLnBvc3RlcmlvciA8LSBhcy5tYXRyaXgoZHRtLm56ICsgay5wcmlvcipnLnByaW9yKQogIAogIGdyb3VwcyA8LSBncm91cHNbIXplcm8uZG9jXQogIGdyb3VwcyA8LSBkcm9wbGV2ZWxzKGdyb3VwcykKICAKICBnLmFkdG0gPC0gYXMubWF0cml4KGFnZ3JlZ2F0ZSh4PWcucG9zdGVyaW9yLGJ5PWxpc3QoZ3JvdXBzPWdyb3VwcyksRlVOPXN1bSlbLC0xXSkKICByb3duYW1lcyhnLmFkdG0pIDwtIGxldmVscyhncm91cHMpCiAgCiAgZy5sYWR0bSA8LSBsb2coZy5hZHRtKQogIAogIGcuZGVsdGEgPC0gdChzY2FsZSggdChzY2FsZShnLmxhZHRtLCBjZW50ZXI9VCwgc2NhbGU9RikpLCBjZW50ZXI9VCwgc2NhbGU9RikpCiAgCiAgZy5hZHRtX3cgPC0gLXN3ZWVwKGcuYWR0bSwxLHJvd1N1bXMoZy5hZHRtKSkgIyB0ZXJtcyBub3QgdyBzcG9rZW4gYnkgawogIGcuYWR0bV9rIDwtIC1zd2VlcChnLmFkdG0sMixjb2xTdW1zKGcuYWR0bSkpICMgdyBzcG9rZW4gYnkgZ3JvdXBzIG90aGVyIHRoYW4gawogIGcuYWR0bV9rdyA8LSBzdW0oZy5hZHRtKSAtIGcuYWR0bV93IC0gZy5hZHRtX2sgLSBnLmFkdG0gIyB0b3RhbCB0ZXJtcyBub3QgdyBvciBrIAogIAogIGcuc2UgPC0gc3FydCgxL2cuYWR0bSArIDEvZy5hZHRtX3cgKyAxL2cuYWR0bV9rICsgMS9nLmFkdG1fa3cpCiAgCiAgZy56ZXRhIDwtIGcuZGVsdGEvZy5zZQogIAogIGcuY291bnRzIDwtIGFzLm1hdHJpeChhZ2dyZWdhdGUoeD1kdG0ubnosIGJ5ID0gbGlzdChncm91cHM9Z3JvdXBzKSwgRlVOPXN1bSlbLC0xXSkKICAKICBpZiAoIWlzLm51bGwocGFpcikpIHsKICAgIHByLmRlbHRhIDwtIHQoc2NhbGUoIHQoc2NhbGUoZy5sYWR0bVtwYWlyLF0sIGNlbnRlciA9IFQsIHNjYWxlID1GKSksIGNlbnRlcj1ULCBzY2FsZT1GKSkKICAgIHByLmFkdG1fdyA8LSAtc3dlZXAoZy5hZHRtW3BhaXIsXSwxLHJvd1N1bXMoZy5hZHRtW3BhaXIsXSkpCiAgICBwci5hZHRtX2sgPC0gLXN3ZWVwKGcuYWR0bVtwYWlyLF0sMixjb2xTdW1zKGcuYWR0bVtwYWlyLF0pKSAjIHcgc3Bva2VuIGJ5IGdyb3VwcyBvdGhlciB0aGFuIGsKICAgIHByLmFkdG1fa3cgPC0gc3VtKGcuYWR0bVtwYWlyLF0pIC0gcHIuYWR0bV93IC0gcHIuYWR0bV9rIC0gZy5hZHRtW3BhaXIsXSAjIHRvdGFsIHRlcm1zIG5vdCB3IG9yIGsKICAgIHByLnNlIDwtIHNxcnQoMS9nLmFkdG1bcGFpcixdICsgMS9wci5hZHRtX3cgKyAxL3ByLmFkdG1fayArIDEvcHIuYWR0bV9rdykKICAgIHByLnpldGEgPC0gcHIuZGVsdGEvcHIuc2UKICAgIAogICAgcmV0dXJuKGxpc3QoemV0YT1wci56ZXRhWzEsXSwgZGVsdGE9cHIuZGVsdGFbMSxdLHNlPXByLnNlWzEsXSwgY291bnRzID0gY29sU3VtcyhkdG0ubnopLCBhY291bnRzID0gY29sU3VtcyhnLmFkdG0pKSkKICB9IGVsc2UgewogICAgcmV0dXJuKGxpc3QoemV0YT1nLnpldGEsZGVsdGE9Zy5kZWx0YSxzZT1nLnNlLGNvdW50cz1nLmNvdW50cyxhY291bnRzPWcuYWR0bSkpCiAgfQp9CgojIyMjIyMjIyMjIyMjIyBGSUdIVElOJyBXT1JEUyBQTE9UVElORyBGVU5DVElPTgoKIyBoZWxwZXIgZnVuY3Rpb24KbWFrZVRyYW5zcGFyZW50PC1mdW5jdGlvbihzb21lQ29sb3IsIGFscGhhPTEwMCkKewogIG5ld0NvbG9yPC1jb2wycmdiKHNvbWVDb2xvcikKICBhcHBseShuZXdDb2xvciwgMiwgZnVuY3Rpb24oY3VyY29sZGF0YSl7cmdiKHJlZD1jdXJjb2xkYXRhWzFdLCBncmVlbj1jdXJjb2xkYXRhWzJdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmx1ZT1jdXJjb2xkYXRhWzNdLGFscGhhPWFscGhhLCBtYXhDb2xvclZhbHVlPTI1NSl9KQp9Cgpmdy5nZ3Bsb3QuZ3JvdXBzIDwtIGZ1bmN0aW9uKGZ3LmNoLCBncm91cHMudXNlID0gYXMuZmFjdG9yKHJvd25hbWVzKGZ3LmNoJHpldGEpKSwgbWF4LndvcmRzID0gNTAsIG1heC5jb3VudHJhbmsgPSA0MDAsIGNvbG9ycGFsZXR0ZT1yZXAoImJsYWNrIixsZW5ndGgoZ3JvdXBzLnVzZSkpLCBzaXplc2NhbGU9MiwgdGl0bGU9IkNvbXBhcmlzb24gb2YgVGVybXMgYnkgR3JvdXBzIiwgc3VidGl0bGUgPSAiIiwgY2FwdGlvbiA9ICJHcm91cC1zcGVjaWZpYyB0ZXJtcyBhcmUgb3JkZXJlZCBieSBGaWdodGluJyBXb3JkcyBzdGF0aXN0aWMgKE1vbnJvZSwgZXQgYWwuIDIwMDgpIikgewogIGlmIChpcy5udWxsKGRpbShmdy5jaCR6ZXRhKSkpIHsjIyB0d28tZ3JvdXAgZncgb2JqZWN0IGNvbnNpc3RzIG9mIHZlY3RvcnMsIG5vdCBtYXRyaWNlcwogICAgemV0YXJhbmttYXQgPC0gY2JpbmQocmFuaygtZncuY2gkemV0YSkscmFuayhmdy5jaCR6ZXRhKSkKICAgIGNvbG5hbWVzKHpldGFyYW5rbWF0KSA8LSBncm91cHMudXNlCiAgICBjb3VudHJhbmsgPC0gcmFuaygtKGZ3LmNoJGNvdW50cykpCiAgfSBlbHNlIHsKICAgIHpldGFyYW5rbWF0IDwtIGFwcGx5KC1mdy5jaCR6ZXRhW2dyb3Vwcy51c2UsXSwxLHJhbmspCiAgICBjb3VudHJhbmsgPC0gcmFuaygtY29sU3Vtcyhmdy5jaCRjb3VudHMpKQogIH0KICB3aWRlcGxvdG1hdCA8LSBhc190aWJibGUoY2JpbmQoemV0YXJhbmttYXQsY291bnRyYW5rPWNvdW50cmFuaykpCiAgd2lkZXBsb3RtYXQkdGVybT1uYW1lcyhjb3VudHJhbmspCiAgI3JhbmtwbG90IDwtIGdhdGhlcih3aWRlcGxvdG1hdCwgcGFydHksIHpldGFyYW5rLCAxOm5jb2woemV0YXJhbmttYXQpKQogIHJhbmtwbG90IDwtIGdhdGhlcih3aWRlcGxvdG1hdCwgZ3JvdXBzLnVzZSwgemV0YXJhbmssIDE6bmNvbCh6ZXRhcmFua21hdCkpCiAgcmFua3Bsb3QkcGxvdHNpemUgPC0gc2l6ZXNjYWxlKig1MC8ocmFua3Bsb3QkemV0YXJhbmspKV4oMS80KQogIHJhbmtwbG90IDwtIHJhbmtwbG90W3JhbmtwbG90JHpldGFyYW5rIDwgbWF4LndvcmRzICsgMSAmIHJhbmtwbG90JGNvdW50cmFuazxtYXguY291bnRyYW5rKzEsXQogIHJhbmtwbG90JGdyb3Vwcy51c2UgPC0gZmFjdG9yKHJhbmtwbG90JGdyb3Vwcy51c2UsbGV2ZWxzPWdyb3Vwcy51c2UpCiAgCiAgcCA8LSBnZ3Bsb3QocmFua3Bsb3QsIGFlcygobnJvdyhyYW5rcGxvdCktY291bnRyYW5rKV4xLCAtKHpldGFyYW5rXjEpLCBjb2xvdXI9Z3JvdXBzLnVzZSkpICsgCiAgICBnZW9tX3BvaW50KHNob3cubGVnZW5kPUYsc2l6ZT1zaXplc2NhbGUvMikgKyAKICAgIHRoZW1lX2NsYXNzaWMoKSArCiAgICB0aGVtZShheGlzLnRpY2tzPWVsZW1lbnRfYmxhbmsoKSwgYXhpcy50ZXh0PWVsZW1lbnRfYmxhbmsoKSApICsKICAgIHlsaW0oLW1heC53b3Jkcyw0MCkgKwogICAgZmFjZXRfZ3JpZChncm91cHMudXNlIH4gLikgKwogICAgZ2VvbV90ZXh0X3JlcGVsKGFlcyhsYWJlbCA9IHRlcm0pLCBzaXplID0gcmFua3Bsb3QkcGxvdHNpemUsIHBvaW50LnBhZGRpbmc9LjA1LAogICAgICAgICAgICAgICAgICAgIGJveC5wYWRkaW5nID0gdW5pdCgwLjIwLCAibGluZXMiKSwgc2hvdy5sZWdlbmQ9RiwgbWF4Lm92ZXJsYXBzID0gSW5mKSArCiAgICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGFscGhhKGNvbG9ycGFsZXR0ZSwgLjcpKSArIAojICAgIGxhYnMoeD0iVGVybXMgdXNlZCBtb3JlIGZyZXF1ZW50bHkgb3ZlcmFsbCDihpIiLCB5PSJUZXJtcyB1c2VkIG1vcmUgZnJlcXVlbnRseSBieSBncm91cCDihpIiLCAgdGl0bGU9dGl0bGUsIHN1YnRpdGxlPXN1YnRpdGxlICwgY2FwdGlvbiA9IGNhcHRpb24pIAogICAgbGFicyh4PXBhc3RlKCJUZXJtcyB1c2VkIG1vcmUgZnJlcXVlbnRseSBvdmVyYWxsIC0tPiIpLCB5PXBhc3RlKCJUZXJtcyB1c2VkIG1vcmUgZnJlcXVlbnRseSBieSBncm91cCAtLT4iKSwgIHRpdGxlPXRpdGxlLCBzdWJ0aXRsZT1zdWJ0aXRsZSAsIGNhcHRpb24gPSBjYXB0aW9uKSAKICAKfQoKb3B0aW9ucyhnZ3JlcGVsLm1heC5vdmVybGFwcyA9IEluZikKCmZ3LmtleXMgPC0gZnVuY3Rpb24oZncuY2gsbi5rZXlzPTEwKSB7CiAgbi5ncm91cHMgPC0gbnJvdyhmdy5jaCR6ZXRhKQogIGtleXMgPC0gbWF0cml4KCIiLG4ua2V5cyxuLmdyb3VwcykKICBjb2xuYW1lcyhrZXlzKSA8LSByb3duYW1lcyhmdy5jaCR6ZXRhKQogIAogIGZvciAoZyBpbiAxOm4uZ3JvdXBzKSB7CiAgICBrZXlzWyxnXSA8LSBuYW1lcyhzb3J0KGZ3LmNoJHpldGFbZyxdLGRlYz1UKVsxOm4ua2V5c10pCiAgfQogIGtleXMKfQpgYGAKCgojIyBDb21wYXJlIEFzc29jaWF0ZWQgUHJlc3MgMTk5NC0yMDEwOiBCZWZvcmUgYW5kIEFmdGVyICJleHRyZW1pc3QiIGFuZCBvdGhlciBxdWVyeSB0ZXJtcwoKKipRdWVyeSBzZWFyY2g6KioKKGFjdGl2aSogfCBhaGJhc2ggfCBha3JvbWl5YSB8IGFuamVtIHwgYW5zaGFydXQgfCBhbnRpY2FwaXRhbCogfCBhbnRpZGVtb2NyKiB8IGFudGllc3RhYmxpc2gqIHwgYW50aWZhIHwgYW50aWdvdmVybiogfCBhbnRpbWlsaXRhciogfCBhbnRpbW9uYXJjaCogfCBhbnRpcGF0cmkqIHwgYW50aXJlbGkqIHwgYW50aXNlbSogfCBhbnRpc29jaWEqIHwgYW50aXN5c3QqIHwgYXBvc3QqIHwgYXRoYXJpcyB8IGF0aGVpKiB8IGF0aGVpc3RzIHwgYXXDn2VycGFybGFtZW50YXJpKiB8IGF1dGhvcml0YXIqIHwgYmFnYXUqIHwgYmlnb3RzIHwgYnBsZiB8IGJ1a2hhcmkqIHwgY2FwaXR1bGF0aW8qIHwgY29uc3BpcmF0byogfCBjb3VudGVyaiogfCBjeWJlcmNhbGlwKiB8IGRhbWlnbyB8IGRhd3JvKiB8IGRlbW9uKiB8IGRlcmFkaWNhbGl6YSogfCBkZXZpYXRpbyogfCBkaXFxaSB8IGRpc3NpZCogfCBkamFtYWF0IHwgZG90YnVzKiB8IGVjb2ZhcyogfCBlc3BvdSogfCBldGhub25hdGlvbmEqIHwgZXh0cmVtKiB8IGZhY2lzdHMgfCBmYWRhaWEqIHwgZmFuYXQqIHwgZmFzY2kqIHwgZmV0w7YgfCBmaWdodGRlbSogfCBmcmVlZG8qIHwgZnVuZGFtZW50YWwqIHwgZnVxcmEgfCBnYWZhdGFyIHwgZ2FtZXJnYSogfCBnZW1pZHppaSB8IGdodWx1d3cgfCBnbG9iYWxpKiB8IGdyYW1zYyogfCBnw7xsZW4qIHwgaGFja3RpdiogfCBoYXF1bmEgfCBoYXJkbGluZSB8IGhhcmthdHVsIHwgaGF0ZW1vbiogfCBoZWltd2UqIHwgaGV6Ym9sKiB8IGhpbmR1cGgqIHwgaGluZHV0dmEgfCBoaXpidXQgfCBob2pqYSogfCBpZGVvbG9nKiB8IGluY2l0ZW0qIHwgaW5jaXRlcnMgfCBpbnN1cnIqIHwgaW50YWN0aSogfCBpc2xhbTR1ayB8IGlzbGFtKiB8IGphbGphbGF0IHwgamJha2MgfCBqaWhhZGkqIHwgam1qYiB8IGpydG4gfCBqdWRhaSogfCBqdWhheW1hbiB8IGp1bmR1KiB8IGthZGl6YSogfCBrYWhhbmlzbSB8IGthaGFuaXN0IHwga2FycmFtKiB8IGtheXNhKiB8IGtoYWxpcyogfCBraGF0bWlhIHwga2hhd2FyaWogfCBraG9tZWluKiB8IGtvdXRsYSB8IGxlZnRpc3QgfCBsZWZ0aXN0cyB8IGxlZnR3aW5nIHwgbGliZXJhdGlvKiB8IG1hZGtoYSogfCBtYWRraGFsKiB8IG1haW1vbmlkKiB8IG1hbm9zcCogfCBtYXVyYXMqIHwgbWNsbiB8IG1pbGl0YW4qIHwgbWlsbGF0dSB8IG1vbmFyYyogfCBtdWRqYSogfCBtdWhhamkqIHwgbXVqYWhpZCogfCBtdXJhYiogfCBtdXR0YWhpKiB8IG5hamphZGFoIHwgbmF0aW9uYWxpKiB8IG5lb2ZhcyogfCBuZW9uYSogfCBvcGFudGlzaCB8IG9wcG9zaXRpKiB8IHBhbGVvbGliZXJ0YXIqIHwgcGFyYW1pbGkqIHwgcGFybGlhbWVudGEqIHwgcGVnaWRhIHwgcG9wdWxpc3QgfCBwcmluY2lwYSogfCBwcm9mZSogfCBwcm93YXIgfCBwdXRpbmlzdCB8IHFhZGFyaSB8IHF1cmFuaXNtIHwgcXVyYW5pc3QgfCBxdXRiaXNtIHwgcXV0YmlzdCB8IHF1dGJpc3RzIHwgcmFjaXMqIHwgcmFkaWNhKiB8IHJlYWN0aW9uaSogfCByZWZvcm1pcyogfCByZWljaHNiw7xyZ2VyYmV3ZSogfCByaWdodGlzdCB8IHJpZ2h0dyogfCByb2ZpcSB8IHJ1c3NvcGgqIHwgc2FiaXJlZW4gfCBzYWRkYSogfCBzYWxhZiogfCBzYXlhZmYgfCBzY3JpcHR1cmEqIHwgc2VjdWxhKiB8IHNlcGFyYXRpb25pKiB8IHNoYXJpYTRob2wqIHwgc2lrcmlraW0gfCBzcGxpdCogfCBzcXVhZGlzbSB8IHN0cmFzc2UqIHwgc3VidmVyKiB8IHN1aWRsYW4qIHwgc3VrYXJuKiB8IHN1cHJlbWEqIHwgc3VwcmVtYWMqIHwgc3ltcGF0aGkqIHwgdGFibGUqIHwgdGFibGlxIHwgdGFrZmlyIHwgdGFrZmlyKiB8IHRha2ZpKiB8dGVycm9yKiB8IHRoZW9jKiB8IHRpdG9pdGUgfCB0cmliYSogfCB0cm90cyogfCB0cm90c2sqIHwgdWtyYWlub3BoKiB8IHVsdHJhY29uc2VydmEqIHwgdWx0cmFsaWIqIHwgdWx0cmFuYXRpb25hKiB8IHVsdHJhciogfCB1c2NtbyB8IHdhaGFiYmkgfCB3YWhhYiogfCB3YWhoYSogfCB4ZW5vcGgqIHwgeXVsZGUqIHwgemlub3ZpZSopCgoKKipMb2FkIGFuZCBjbGVhbiB0aGUgZGF0YSoqCgogICogdG8gc3RyaW5nICYgbG93ZXIgdGV4dAogICogcGl2b3QgdG8gbG9uZyBmb3JtYXQKICAqIGFwcGx5IHRleHRfY2xlYW5lciB0byBvbmUgY29sdW1uICJjb250ZXh0LnRleHQiCgpgYGB7ciwgIHJlc3VsdHM9J2FzaXMnfQp0ZXh0X2NsZWFuZXI8LWZ1bmN0aW9uKGNvcnB1cyl7CiAgdGVtcGNvcnB1czwtQ29ycHVzKFZlY3RvclNvdXJjZShjb3JwdXMpKQogIHRlbXBjb3JwdXM8LXRtX21hcCh0ZW1wY29ycHVzLAogICAgICAgICAgICAgICAgICAgIHJlbW92ZVB1bmN0dWF0aW9uKQogIHRlbXBjb3JwdXM8LXRtX21hcCh0ZW1wY29ycHVzLAogICAgICAgICAgICAgICAgICAgIHN0cmlwV2hpdGVzcGFjZSkKICB0ZW1wY29ycHVzPC10bV9tYXAodGVtcGNvcnB1cywKICAgICAgICAgICAgICAgICAgICByZW1vdmVOdW1iZXJzKQogIHRlbXBjb3JwdXM8LXRtX21hcCh0ZW1wY29ycHVzLAogICAgICAgICAgICAgICAgICAgICByZW1vdmVXb3Jkcywgc3RvcHdvcmRzKCJlbmdsaXNoIikpCiAgdGVtcGNvcnB1czwtdG1fbWFwKHRlbXBjb3JwdXMsIAogICAgICAgICAgICAgICAgICAgIHN0ZW1Eb2N1bWVudCkKICByZXR1cm4odGVtcGNvcnB1cykKfQoKYGBgCgpgYGB7ciwgZWNobz1GQUxTRSwgcmVzdWx0cz0gRkFMU0V9CgpleHRyZW1fTllULmRmbV9hbGwgPC1yZWFkLmRlbGltKCJ+L0RvY3VtZW50cy9HaXRIdWIvNzBjb3JyX2V4dHJlbWlzdF85NDEwX05ZVC50eHQiLCBoZWFkZXI9VFJVRSwgc2VwPSJcdCIpCmV4dHJlbV9OWVQuZGZtX2FsbCRwdWJkYXRlID0gc3Vic3RyKGV4dHJlbV9OWVQuZGZtX2FsbCRUZXh0LklELDksMTYpCmV4dHJlbV9OWVQuZGZtX2FsbCRwdWJkYXRlIDwtIGFzLlBPU0lYY3QoZXh0cmVtX05ZVC5kZm1fYWxsJHB1YmRhdGUsIGZvcm1hdCA9ICIlWSVtJWQiKQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCnNldC5zZWVkKDIyMTcpCmV4dHJlbV9OWVQuZGZtIDwtIGFzLmRhdGEuZnJhbWUoc2FtcGxlX24oZXh0cmVtX05ZVC5kZm1fYWxsLCAxNTAwMCkpCnJtKGV4dHJlbV9OWVQuZGZtX2FsbCkKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIGV4dHJlbV9OWVQuZGZtJENvbnRleHQuYmVmb3JlID0gbGFwcGx5KGV4dHJlbV9OWVQuZGZtJENvbnRleHQuYmVmb3JlLCB0b1N0cmluZykKIyBleHRyZW1fTllULmRmbSRDb250ZXh0LmJlZm9yZSA9IGxhcHBseShleHRyZW1fTllULmRmbSRDb250ZXh0LmJlZm9yZSwgdG9sb3dlcikKIyAKIyBleHRyZW1fTllULmRmbSRDb250ZXh0LmFmdGVyID0gbGFwcGx5KGV4dHJlbV9OWVQuZGZtJENvbnRleHQuYWZ0ZXIsIHRvU3RyaW5nKQojIGV4dHJlbV9OWVQuZGZtJENvbnRleHQuYWZ0ZXIgPSBsYXBwbHkoZXh0cmVtX05ZVC5kZm0kQ29udGV4dC5hZnRlciwgdG9sb3dlcikKCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKZXh0cmVtX05ZVC5kZm0gPC0gZXh0cmVtX05ZVC5kZm0gJT4lIGRpc3RpbmN0KENvbnRleHQuYmVmb3JlLCAua2VlcF9hbGwgPSBUUlVFKQoKZXh0cmVtX05ZVC5kZm0ubG9uZyA8LSBwaXZvdF9sb25nZXIoZXh0cmVtX05ZVC5kZm0sIGNvbHM9YyhDb250ZXh0LmJlZm9yZSwgQ29udGV4dC5hZnRlciksIG5hbWVzX3RvID0gIkNvbnRleHQiLCB2YWx1ZXNfdG8gPSAiY29udGV4dC50ZXh0IikKCmV4dHJlbV9OWVQuZGZtLmxvbmckQ29udGV4dCA8LSBhcy5mYWN0b3IoZXh0cmVtX05ZVC5kZm0ubG9uZyRDb250ZXh0KQoKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgpleHRyZW1lY29ycHVzIDwtdGV4dF9jbGVhbmVyKGV4dHJlbV9OWVQuZGZtLmxvbmckY29udGV4dC50ZXh0KQoKYGBgCgoKQ2FsY3VsYXRlIEZXLgoKYGBge3IsIG1lc3NhZ2U9RkFMU0V9CgplIDwtIGRmbShleHRyZW1lY29ycHVzJGNvbnRlbnQpCm1lc3NhZ2UoZGltKGUpKQpoZWFkKGUpCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCmUgPC0gZGZtX3NlbGVjdChlLCBwYXR0ZXJuID0gc3RvcHdvcmRzKCJlbmdsaXNoIiksIHNlbGVjdGlvbiA9ICJyZW1vdmUiKQplIDwtIGRmbV9zZWxlY3QoZSwgbWluX25jaGFyID0gMikKZSA8LSBkZm1fdHJpbShlLCBtaW5fdGVybWZyZXEgPSA0LCBtaW5fZG9jZnJlcSA9IC4wNSwgdmVyYm9zZT1UUlVFKQoKI2RpbShlKQojIHNwYXJzaXR5KGUpCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCmV4dHJlbV9kdG0gPC0gY29udmVydChlLCB0bz0nZGF0YS5mcmFtZScpCmV4dHJlbV9kdG0gPC0gZXh0cmVtX2R0bVstYygxKV0KdyA8LSB3aGljaCggc2FwcGx5KGV4dHJlbV9kdG0sIGNsYXNzICkgPT0gJ2NoYXJhY3RlcicgKQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgpmdy5leHRyZW0gPC0gZndncm91cHMoZXh0cmVtX2R0bSwgZ3JvdXBzPWV4dHJlbV9OWVQuZGZtLmxvbmckQ29udGV4dCkKCnJtKGV4dHJlbV9kdG0pCgpgYGAKCgoqKkdldCBhbmQgc2hvdyB0aGUgdG9wIHdvcmRzIHBlciBncm91cCBieSB6ZXRhLioqCgpgYGB7ciBlY2hvPVRSVUUsIHJlc3VsdHM9ImFzaXMifQoKZndrZXlzLmV4dHJlbSA8LSBmdy5rZXlzKGZ3LmV4dHJlbSwgbi5rZXlzPTIwKQpjb2xzIDwtIHJldihjb2xuYW1lcyhmd2tleXMuZXh0cmVtKSkKZndrZXlzLmV4dHJlbSA8LSBmd2tleXMuZXh0cmVtWyxjb2xzXQprYWJsZShmd2tleXMuZXh0cmVtKQoKYGBgCgpQbG90OiBCZWZvcmUgaW4gQmx1ZSwgQWZ0ZXIgaW4gUmVkCgpgYGB7ciwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9NH0KCnAuZncuZXh0cmVtIDwtIGZ3LmdncGxvdC5ncm91cHMoZncuZXh0cmVtLHNpemVzY2FsZT00LG1heC53b3Jkcz0yMDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4LmNvdW50cmFuaz00MDAsY29sb3JwYWxldHRlPWMoInJlZCIsImJsdWUiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aXRsZSA9ICdDb21wYXJpc29uIG9mIFRlcm1zIEJlZm9yZSBhbmQgQWZ0ZXIgUXVlcnkgV29yZCcpCnAuZncuZXh0cmVtCmBgYAoKIyMgQ2FsY3VsYXRlIGJ5IHF1ZXJ5IGl0ZW0vc2VhcmNoIHRlcm0KCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD00fQoKZXh0cmVtX05ZVC5kZm0ubG9uZyRRdWVyeS5pdGVtIDwtIGFzLmZhY3RvcihleHRyZW1fTllULmRmbS5sb25nJFF1ZXJ5Lml0ZW0pCgp0b3BfbiA8LWFzLmRhdGEuZnJhbWUoc29ydCh0YWJsZShleHRyZW1fTllULmRmbS5sb25nJFF1ZXJ5Lml0ZW0pLCBkZWNyZWFzaW5nID0gVFJVRSlbMTo1XSkgCm1lc3NhZ2UoZGltKHRvcF9uKSkKY29sbmFtZXModG9wX24pIDwtIGMoJ3Rlcm0nLCAnRnJlcScpCm1lc3NhZ2UodG9wX24pCgpleHRyZW1fZHRtX3RvcG4gPC0gY29udmVydChlLCB0bz0nZGF0YS5mcmFtZScpCmV4dHJlbV9kdG1fdG9wbiROdW1iZXIub2YuaGl0IDwtIGV4dHJlbV9OWVQuZGZtLmxvbmckTnVtYmVyLm9mLmhpdAoKdG9wbl90ZXJtcyA8LSBleHRyZW1fTllULmRmbS5sb25nICU+JQogICAgICBmaWx0ZXIoUXVlcnkuaXRlbSAlaW4lIHRvcF9uJHRlcm0pCgpleHRyZW1fZHRtX3RvcG5fa2VlcCA8LSBleHRyZW1fZHRtX3RvcG4gJT4lIAogICAgZmlsdGVyKE51bWJlci5vZi5oaXQgJWluJSB0b3BuX3Rlcm1zJE51bWJlci5vZi5oaXQpCgpyIDwtIHN1bShsZW5ndGgoZXh0cmVtX2R0bV90b3BuX2tlZXApKQoKZXh0cmVtX2R0bV90b3BuX2tlZXAgPC0gZXh0cmVtX2R0bV90b3BuX2tlZXBbLWMoMSwgcildCgpmdy5xdWVyeV9pdGVtIDwtIGZ3Z3JvdXBzKGV4dHJlbV9kdG1fdG9wbl9rZWVwLGdyb3VwcyA9IHRvcG5fdGVybXMkUXVlcnkuaXRlbSkKZndrZXlzLnF1ZXJ5X2l0ZW0gPC0gZncua2V5cyhmdy5xdWVyeV9pdGVtLCBuLmtleXM9MTUpCmthYmxlKGZ3a2V5cy5xdWVyeV9pdGVtKQoKcm0ocikKcm0oZXh0cmVtX2R0bV90b3BuKQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCnAuZncucXVlcnlfaXRlbSA8LSBmdy5nZ3Bsb3QuZ3JvdXBzKGZ3LnF1ZXJ5X2l0ZW0sc2l6ZXNjYWxlPTMuMixtYXgud29yZHM9MTUwLG1heC5jb3VudHJhbms9NDAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvcnBhbGV0dGU9YygiZGFya2dyZWVuIiwiZGFya2dyZWVuIiwiZGFya2dyZWVuIiwiZGFya2dyZWVuIiwiZGFya2dyZWVuIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlID0gJ0NvbXBhcmlzb24gb2YgVGVybXMgYnkgT3ZlcmFsbCBUb3AgVGVybXMnKQpwLmZ3LnF1ZXJ5X2l0ZW0KCmBgYAoKCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9NH0KCmV4dHJlbV9kdG1fdG9wbiA8LSBjb252ZXJ0KGUsIHRvPSdkYXRhLmZyYW1lJykKZXh0cmVtX2R0bV90b3BuJE51bWJlci5vZi5oaXQgPC0gZXh0cmVtX05ZVC5kZm0ubG9uZyROdW1iZXIub2YuaGl0CmV4dHJlbV9kdG1fdG9wbiRDb250ZXh0IDwtIGV4dHJlbV9OWVQuZGZtLmxvbmckQ29udGV4dAoKdG9wbl90ZXJtcyA8LSBleHRyZW1fTllULmRmbS5sb25nICU+JQogICAgICBmaWx0ZXIoUXVlcnkuaXRlbSAlaW4lIHRvcF9uJHRlcm0pCgpleHRyZW1fZHRtX3RvcG5fa2VlcCA8LSBleHRyZW1fZHRtX3RvcG4gJT4lIAogICAgZmlsdGVyKE51bWJlci5vZi5oaXQgJWluJSB0b3BuX3Rlcm1zJE51bWJlci5vZi5oaXQpCgpleHRyZW1fZHRtX3RvcG5fa2VlcF9iZWZvcmUgPC0gZXh0cmVtX2R0bV90b3BuX2tlZXBbZ3JlcCgiYmVmb3JlIixleHRyZW1fZHRtX3RvcG5fa2VlcCRDb250ZXh0KSxdCmV4dHJlbV9kdG1fdG9wbl9rZWVwX2FmdGVyIDwtIGV4dHJlbV9kdG1fdG9wbl9rZWVwW2dyZXAoImFmdGVyIiwgZXh0cmVtX2R0bV90b3BuX2tlZXAkQ29udGV4dCksXQoKcnIgPC1kaW0odG9wbl90ZXJtcylbMV0KciA8LSBzdW0obGVuZ3RoKGV4dHJlbV9kdG1fdG9wbl9rZWVwX2JlZm9yZSkpCnRvcG5fdGVybXNfYmVmb3JlIDwtIHRvcG5fdGVybXNbc2VxKDEscnIsMiksXQp0b3BuX3Rlcm1zX2FmdGVyIDwtIHRvcG5fdGVybXNbc2VxKDIscnIsMiksXQoKZXh0cmVtX2R0bV90b3BuX2tlZXBfYmVmb3JlIDwtIGV4dHJlbV9kdG1fdG9wbl9rZWVwX2JlZm9yZVstYygxLCByLTEsIHIpXQpleHRyZW1fZHRtX3RvcG5fa2VlcF9hZnRlciA8LSBleHRyZW1fZHRtX3RvcG5fa2VlcF9hZnRlclstYygxLCByLTEsIHIpXQoKcm0ocnIpCnJtKHIpCnJtKGV4dHJlbV9kdG1fdG9wbikKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpmdy5xdWVyeV9pdGVtX2JlZm9yZSA8LSBmd2dyb3VwcyhleHRyZW1fZHRtX3RvcG5fa2VlcF9iZWZvcmUsZ3JvdXBzID0gdG9wbl90ZXJtc19iZWZvcmUkUXVlcnkuaXRlbSkKZndrZXlzLnF1ZXJ5X2l0ZW1fYmVmb3JlIDwtIGZ3LmtleXMoZncucXVlcnlfaXRlbV9iZWZvcmUsIG4ua2V5cz0xNSkKa2FibGUoZndrZXlzLnF1ZXJ5X2l0ZW1fYmVmb3JlLCBjYXB0aW9uID0gIlRvcCAxNSBXb3JkcyBmb3IgUXVlcnkgVGVybTogQkVGT1JFIikKCnAuZncucXVlcnlfaXRlbV9iZWZvcmUgPC0gZncuZ2dwbG90Lmdyb3Vwcyhmdy5xdWVyeV9pdGVtX2JlZm9yZSxzaXplc2NhbGU9MixtYXgud29yZHM9MTUwLG1heC5jb3VudHJhbms9NDAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3JwYWxldHRlID0gYygnYmx1ZScsJ2JsdWUnLCdibHVlJywgJ2JsdWUnLCdibHVlJyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aXRsZSA9ICdDb21wYXJpc29uIG9mIFRlcm1zIGJ5IE92ZXJhbGwgVG9wIFRlcm1zOiBCRUZPUkUnKQpwLmZ3LnF1ZXJ5X2l0ZW1fYmVmb3JlCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKZncucXVlcnlfaXRlbV9hZnRlciA8LSBmd2dyb3VwcyhleHRyZW1fZHRtX3RvcG5fa2VlcF9hZnRlcixncm91cHMgPSB0b3BuX3Rlcm1zX2FmdGVyJFF1ZXJ5Lml0ZW0pCmZ3a2V5cy5xdWVyeV9pdGVtX2FmdGVyIDwtIGZ3LmtleXMoZncucXVlcnlfaXRlbV9hZnRlciwgbi5rZXlzPTE1KQprYWJsZShmd2tleXMucXVlcnlfaXRlbV9hZnRlciwgY2FwdGlvbiA9ICJUb3AgMTUgV29yZHMgZm9yIFF1ZXJ5IFRlcm06IEFGVEVSIikKCnAuZncucXVlcnlfaXRlbV9hZnRlciA8LSBmdy5nZ3Bsb3QuZ3JvdXBzKGZ3LnF1ZXJ5X2l0ZW1fYWZ0ZXIsc2l6ZXNjYWxlPTIsbWF4LndvcmRzPTE1MCxtYXguY291bnRyYW5rPTQwMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9ycGFsZXR0ZSA9IGMoJ3JlZCcsICdyZWQnLCdyZWQnLCdyZWQnLCdyZWQnKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGl0bGUgPSAnQ29tcGFyaXNvbiBvZiBUZXJtcyBieSBPdmVyYWxsIFRvcCBUZXJtczogQUZURVInKQpwLmZ3LnF1ZXJ5X2l0ZW1fYWZ0ZXIKCgpgYGAKCgoKIyMgQ2FsY3VsYXRlIFBhcnRzIG9mIHNwZWVjaCBieSBiZWZvcmUgYW5kIGFmdGVyCgpDYWxjdWxhdGUgRlcgYW5kIGtleXMKYGBge3IsIHJlc3VsdHM9J2hpZGUnLCB3YXJuaW5nPUZBTFNFfQoKdWRfbW9kZWwgPC0gdWRwaXBlX2xvYWRfbW9kZWwodWRfbW9kZWwkZmlsZV9tb2RlbCkKCnR4dCA8LWFzLmNoYXJhY3RlcihleHRyZW1fTllULmRmbS5sb25nJGNvbnRleHQudGV4dCkKCnhfdWRwIDwtIHVkcGlwZV9hbm5vdGF0ZSh1ZF9tb2RlbCwgeCA9IHR4dCwgZG9jX2lkID0gc2VxX2Fsb25nKHR4dCkpCnggPC0gYXMuZGF0YS5mcmFtZSh4X3VkcCkKCngkZG9jX2lkIDwtYXMuaW50ZWdlcih4JGRvY19pZCkKCnhfb2RkLmJlZm9yZSA8LSB4W3gkZG9jX2lkICUlIDIgPT0gMSxdCnhfZXZlbi5hZnRlciA8LXhbeCRkb2NfaWQgJSUgMiA9PSAwLCBdCgpgYGAKCgoqQSBmZXcgYmFyY2hhcnQgZnVuY3Rpb25zKgoKYGBge3IsIHJlc3VsdHM9J2hpZGUnfQoKIyMgVU5JVkVSU0FMIFBvUwpVUE9TX2JhcmNoYXJ0IDwtIGZ1bmN0aW9uKGRmMSwgZGYyKXsKICBzdGF0czEgPC0gdHh0X2ZyZXEoZGYxJHVwb3MpCiAgc3RhdHMxJGtleSA8LSBmYWN0b3Ioc3RhdHMxJGtleSwgbGV2ZWxzID0gcmV2KHN0YXRzMSRrZXkpKQogIAogIHN0YXRzMiA8LSB0eHRfZnJlcShkZjIkdXBvcykKICBzdGF0czIka2V5IDwtIGZhY3RvcihzdGF0czIka2V5LCBsZXZlbHMgPSByZXYoc3RhdHMyJGtleSkpCiAgCiAgYyhiYXJjaGFydChrZXkgfiBmcmVxLCBkYXRhID0gc3RhdHMxLCBjb2wgPSAiY2FkZXRibHVlIiwgCiAgICAgICAgbWFpbiA9ICJVUE9TIChVbml2ZXJzYWwgUGFydHMgb2YgU3BlZWNoKVxuIGZyZXF1ZW5jeSBvZiBvY2N1cnJlbmNlOiBCRUZPUkUgdnMgQUZURVIiLCAKICAgICAgICAgeGxhYiA9ICJGcmVxIiksIAogICAgYmFyY2hhcnQoa2V5IH4gZnJlcSwgZGF0YSA9IHN0YXRzMiwgY29sID0gICdza3libHVlJywKICAgICAgICAgeGxhYiA9ICJGcmVxIikpCn0KCgoKIyMgTk9VTlMKTk9VTlNfYmFyY2hhcnQgPC0gZnVuY3Rpb24oZGYxLCBkZjIpewogIAogIHN0YXRzMSA8LSBzdWJzZXQoZGYxLCB1cG9zICVpbiUgYygiTk9VTiIpKSAKICBzdGF0czEgPC0gdHh0X2ZyZXEoc3RhdHMxJHRva2VuKQogIHN0YXRzMSRrZXkgPC0gZmFjdG9yKHN0YXRzMSRrZXksIGxldmVscyA9IHJldihzdGF0czEka2V5KSkKICAKICBzdGF0czIgPC0gc3Vic2V0KGRmMiwgdXBvcyAlaW4lIGMoIk5PVU4iKSkgCiAgc3RhdHMyIDwtIHR4dF9mcmVxKHN0YXRzMiR0b2tlbikKICBzdGF0czIka2V5IDwtIGZhY3RvcihzdGF0czIka2V5LCBsZXZlbHMgPSByZXYoc3RhdHMyJGtleSkpCiAgCiAgYyhiYXJjaGFydChrZXkgfiBmcmVxLCBkYXRhID0gaGVhZChzdGF0czEsIDIwKSwgY29sID0gImNhZGV0Ymx1ZSIsIAogICAgICAgICAgIG1haW4gPSAiTW9zdCBvY2N1cnJpbmcgbm91bnM6IEJFRk9SRSB2cyBBRlRFUiIsIHhsYWIgPSAiRnJlcSIpLAogICAgICBiYXJjaGFydChrZXkgfiBmcmVxLCBkYXRhID0gaGVhZChzdGF0czIsIDIwKSwgY29sID0gInNreWJsdWUiLCAKICAgICAgICAgICAgeGxhYiA9ICJGcmVxIikpCn0KCiMjIEFESkVDVElWRVMKQURKX2JhcmNoYXJ0IDwtIGZ1bmN0aW9uKGRmMSwgZGYyKXsKICAKICBzdGF0czEgPC0gc3Vic2V0KGRmMSwgdXBvcyAlaW4lIGMoIkFESiIpKSAKICBzdGF0czEgPC0gdHh0X2ZyZXEoc3RhdHMxJHRva2VuKQogIHN0YXRzMSRrZXkgPC0gZmFjdG9yKHN0YXRzMSRrZXksIGxldmVscyA9IHJldihzdGF0czEka2V5KSkKICAKICBzdGF0czIgPC0gc3Vic2V0KGRmMiwgdXBvcyAlaW4lIGMoIkFESiIpKSAKICBzdGF0czIgPC0gdHh0X2ZyZXEoc3RhdHMyJHRva2VuKQogIHN0YXRzMiRrZXkgPC0gZmFjdG9yKHN0YXRzMiRrZXksIGxldmVscyA9IHJldihzdGF0czIka2V5KSkKICAKICBjKGJhcmNoYXJ0KGtleSB+IGZyZXEsIGRhdGEgPSBoZWFkKHN0YXRzMSwgMjApLCBjb2wgPSAiY2FkZXRibHVlIiwgCiAgICAgICAgICAgbWFpbiA9ICJNb3N0IG9jY3VycmluZyBhZGplY3RpdmVzOiBCRUZPUkUgdnMgQUZURVIiLCB4bGFiID0gIkZyZXEiKSwKICAgICAgYmFyY2hhcnQoa2V5IH4gZnJlcSwgZGF0YSA9IGhlYWQoc3RhdHMyLCAyMCksIGNvbCA9ICJza3libHVlIiwgCiAgICAgICAgIHhsYWIgPSAiRnJlcSIpKQp9CgojIyBVc2luZyBSQUtFIHRvIGZpbmQga2V5d29yZHMKUkFLRV9LV19iYXJjaGFydCA8LSBmdW5jdGlvbihkZjEsZGYyKXsKICAKICBzdGF0czEgPC0ga2V5d29yZHNfcmFrZSh4ID0gZGYxLCB0ZXJtID0gImxlbW1hIiwgZ3JvdXAgPSAiZG9jX2lkIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICByZWxldmFudCA9IGRmMSR1cG9zICVpbiUgYygiTk9VTiIsICJBREoiKSkKICBzdGF0czEka2V5IDwtIGZhY3RvcihzdGF0czEka2V5d29yZCwgbGV2ZWxzID0gcmV2KHN0YXRzMSRrZXl3b3JkKSkKICAKICBzdGF0czIgPC0ga2V5d29yZHNfcmFrZSh4ID0gZGYyLCB0ZXJtID0gImxlbW1hIiwgZ3JvdXAgPSAiZG9jX2lkIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICByZWxldmFudCA9IGRmMiR1cG9zICVpbiUgYygiTk9VTiIsICJBREoiKSkKICBzdGF0czIka2V5IDwtIGZhY3RvcihzdGF0czIka2V5d29yZCwgbGV2ZWxzID0gcmV2KHN0YXRzMiRrZXl3b3JkKSkKICAKICAKICBjKGJhcmNoYXJ0KGtleSB+IHJha2UsIGRhdGEgPSBoZWFkKHN1YnNldChzdGF0czEsIGZyZXEgPiAzKSwgMjApLCBjb2wgPSAiY2FkZXRibHVlIiwgCiAgICAgICAgICAgbWFpbiA9ICJLZXl3b3JkcyBpZGVudGlmaWVkIGJ5IFJBS0U6IEJFRk9SRSB2cyBBRlRFUiIsIAogICAgICAgICAgIHhsYWIgPSAiUmFrZSIpLAogICAgYmFyY2hhcnQoa2V5IH4gcmFrZSwgZGF0YSA9IGhlYWQoc3Vic2V0KHN0YXRzMiwgZnJlcSA+IDMpLCAyMCksIGNvbCA9ICJza3libHVlIiwgCiAgICAgICAgICAgeGxhYiA9ICJSYWtlIikpCn0KCiMjIFVzaW5nIFBvaW50d2lzZSBNdXR1YWwgSW5mb3JtYXRpb24gQ29sbG9jYXRpb25zClBXSV9iYXJjaGFydCA8LSBmdW5jdGlvbihkZjEsIGRmMil7CiAgCiAgZGYxJHdvcmQgPC0gdG9sb3dlcihkZjEkdG9rZW4pCiAgc3RhdHMxIDwtIGtleXdvcmRzX2NvbGxvY2F0aW9uKHggPSBkZjEsIHRlcm0gPSAid29yZCIsIGdyb3VwID0gImRvY19pZCIpCiAgc3RhdHMxJGtleSA8LSBmYWN0b3Ioc3RhdHMxJGtleXdvcmQsIGxldmVscyA9IHJldihzdGF0czEka2V5d29yZCkpCiAgCiAgZGYyJHdvcmQgPC0gdG9sb3dlcihkZjIkdG9rZW4pCiAgc3RhdHMyIDwtIGtleXdvcmRzX2NvbGxvY2F0aW9uKHggPSBkZjIsIHRlcm0gPSAid29yZCIsIGdyb3VwID0gImRvY19pZCIpCiAgc3RhdHMyJGtleSA8LSBmYWN0b3Ioc3RhdHMyJGtleXdvcmQsIGxldmVscyA9IHJldihzdGF0czIka2V5d29yZCkpCiAgCiAgYyhiYXJjaGFydChrZXkgfiBwbWksIGRhdGEgPSBoZWFkKHN1YnNldChzdGF0czEsIGZyZXEgPiAzKSwgMjApLCBjb2wgPSAiY2FkZXRibHVlIiwgCiAgICAgICAgICAgbWFpbiA9ICJLZXl3b3JkcyBpZGVudGlmaWVkIGJ5IFBNSSBDb2xsb2NhdGlvbjogQkVGT1JFIHZzIEFGVEVSIiwgCiAgICAgICAgICAgeGxhYiA9ICJQTUkgKFBvaW50d2lzZSBNdXR1YWwgSW5mb3JtYXRpb24pIiksCiAgICAgIGJhcmNoYXJ0KGtleSB+IHBtaSwgZGF0YSA9IGhlYWQoc3Vic2V0KHN0YXRzMiwgZnJlcSA+IDMpLCAyMCksIGNvbCA9ICJza3libHVlIiwgCiAgICAgICAgICAgeGxhYiA9ICJQTUkgKFBvaW50d2lzZSBNdXR1YWwgSW5mb3JtYXRpb24pIikpCn0KCiMjIFVzaW5nIGEgc2VxdWVuY2Ugb2YgUE9TIHRhZ3MgKG5vdW4gcGhyYXNlcyAvIHZlcmIgcGhyYXNlcykKUE9TX2JhcmNoYXJ0IDwtIGZ1bmN0aW9uKGRmMSwgZGYyKXsKICAKICBkZjEkcGhyYXNlX3RhZyA8LSBhc19waHJhc2VtYWNoaW5lKGRmMSR1cG9zLCB0eXBlID0gInVwb3MiKQogIHN0YXRzMSA8LSBrZXl3b3Jkc19waHJhc2VzKHggPSBkZjEkcGhyYXNlX3RhZywgdGVybSA9IHRvbG93ZXIoZGYxJHRva2VuKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuID0gIihBfE4pKk4oUCtEKihBfE4pKk4pKiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgaXNfcmVnZXggPSBUUlVFLCBkZXRhaWxlZCA9IEZBTFNFKQogIHN0YXRzMSA8LSBzdWJzZXQoc3RhdHMxLCBuZ3JhbSA+IDEgJiBmcmVxID4gMykKICBzdGF0czEka2V5IDwtIGZhY3RvcihzdGF0czEka2V5d29yZCwgbGV2ZWxzID0gcmV2KHN0YXRzMSRrZXl3b3JkKSkKICAKICBkZjIkcGhyYXNlX3RhZyA8LSBhc19waHJhc2VtYWNoaW5lKGRmMiR1cG9zLCB0eXBlID0gInVwb3MiKQogIHN0YXRzMiA8LSBrZXl3b3Jkc19waHJhc2VzKHggPSBkZjIkcGhyYXNlX3RhZywgdGVybSA9IHRvbG93ZXIoZGYyJHRva2VuKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuID0gIihBfE4pKk4oUCtEKihBfE4pKk4pKiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgaXNfcmVnZXggPSBUUlVFLCBkZXRhaWxlZCA9IEZBTFNFKQogIHN0YXRzMiA8LSBzdWJzZXQoc3RhdHMyLCBuZ3JhbSA+IDEgJiBmcmVxID4gMykKICBzdGF0czIka2V5IDwtIGZhY3RvcihzdGF0czIka2V5d29yZCwgbGV2ZWxzID0gcmV2KHN0YXRzMiRrZXl3b3JkKSkKICAKICBjKGJhcmNoYXJ0KGtleSB+IGZyZXEsIGRhdGEgPSBoZWFkKHN0YXRzMSwgMjApLCBjb2wgPSAiY2FkZXRibHVlIiwgCiAgICAgICAgICAgbWFpbiA9ICJLZXl3b3JkcyAtIHNpbXBsZSBub3VuIHBocmFzZXM6IEJFRk9SRSB2cyBBRlRFUiIsIHhsYWIgPSAiRnJlcXVlbmN5IiksCiAgICAgIGJhcmNoYXJ0KGtleSB+IGZyZXEsIGRhdGEgPSBoZWFkKHN0YXRzMiwgMjApLCBjb2wgPSAic2t5Ymx1ZSIsIAogICAgICAgICAgICAgICB4bGFiID0gIkZyZXF1ZW5jeSIpKQp9CmBgYAoKCiMjIEJhciBDaGFydHMgZnJvbSBGdW5jdGlvbnMgQWJvdmUKCmBgYHtyfQoKVVBPU19iYXJjaGFydCh4X29kZC5iZWZvcmUsIHhfZXZlbi5hZnRlcikKTk9VTlNfYmFyY2hhcnQoeF9vZGQuYmVmb3JlLCB4X2V2ZW4uYWZ0ZXIpCkFESl9iYXJjaGFydCh4X29kZC5iZWZvcmUsIHhfZXZlbi5hZnRlcikKUkFLRV9LV19iYXJjaGFydCh4X29kZC5iZWZvcmUsIHhfZXZlbi5hZnRlcikKUFdJX2JhcmNoYXJ0KHhfb2RkLmJlZm9yZSwgeF9ldmVuLmFmdGVyKQpQT1NfYmFyY2hhcnQoeF9vZGQuYmVmb3JlLCB4X2V2ZW4uYWZ0ZXIpCgpgYGAKCgojIyBDb29jY3VyZW5jZXMKCmBgYHtyfQoKQ09fT0Nfbm91bl9hZGpfc2FtZV9zZW50LmJlZm9yZSA8LSBmdW5jdGlvbihkZjEpewogIAogIGxpYnJhcnkoaWdyYXBoKQogIGxpYnJhcnkoZ2dyYXBoKQogIGxpYnJhcnkoZ2dwbG90MikKICAKICBjb29jIDwtIGNvb2NjdXJyZW5jZSh4ID0gc3Vic2V0KGRmMSwgdXBvcyAlaW4lIGMoIk5PVU4iLCAiQURKIikpLCAKICAgICAgICAgICAgICAgICAgICAgICB0ZXJtID0gImxlbW1hIiwgCiAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBjKCJkb2NfaWQiLCAicGFyYWdyYXBoX2lkIiwgInNlbnRlbmNlX2lkIikpCgogIHdvcmRuZXR3b3JrIDwtIGhlYWQoY29vYywgNjApCiAgd29yZG5ldHdvcmsgPC0gZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKHdvcmRuZXR3b3JrKQogIAogIGdncmFwaCh3b3JkbmV0d29yaywgbGF5b3V0ID0gImZyIikgKwogICAgZ2VvbV9lZGdlX2xpbmsoYWVzKHdpZHRoID0gY29vYywgZWRnZV9hbHBoYSA9IGNvb2MpLCBlZGdlX2NvbG91ciA9ICJwaW5rIikgKwogICAgZ2VvbV9ub2RlX3RleHQoYWVzKGxhYmVsID0gbmFtZSksIGNvbCA9ICJkYXJrZ3JlZW4iLCBzaXplID0gNCkgKwogICAgdGhlbWVfZ3JhcGgoYmFzZV9mYW1pbHkgPSAiQXJpYWwgTmFycm93IikgKwogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgICBsYWJzKHRpdGxlID0gIkNvb2NjdXJyZW5jZXMgd2l0aGluIHNlbnRlbmNlOiBCRUZPUkUiLCBzdWJ0aXRsZSA9ICJOb3VucyAmIEFkamVjdGl2ZSIpCiAgCn0KCkNPX09DX25vdW5fYWRqX3NhbWVfc2VudC5hZnRlciA8LSBmdW5jdGlvbihkZjIpewogIAogIGxpYnJhcnkoaWdyYXBoKQogIGxpYnJhcnkoZ2dyYXBoKQogIGxpYnJhcnkoZ2dwbG90MikKICAKICBjb29jIDwtIGNvb2NjdXJyZW5jZSh4ID0gc3Vic2V0KGRmMiwgdXBvcyAlaW4lIGMoIk5PVU4iLCAiQURKIikpLCAKICAgICAgICAgICAgICAgICAgICAgICB0ZXJtID0gImxlbW1hIiwgCiAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBjKCJkb2NfaWQiLCAicGFyYWdyYXBoX2lkIiwgInNlbnRlbmNlX2lkIikpCgogIHdvcmRuZXR3b3JrIDwtIGhlYWQoY29vYywgNjApCiAgd29yZG5ldHdvcmsgPC0gZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKHdvcmRuZXR3b3JrKQogIAogIGdncmFwaCh3b3JkbmV0d29yaywgbGF5b3V0ID0gImZyIikgKwogICAgZ2VvbV9lZGdlX2xpbmsoYWVzKHdpZHRoID0gY29vYywgZWRnZV9hbHBoYSA9IGNvb2MpLCBlZGdlX2NvbG91ciA9ICJsaWdodGdyZWVuIikgKwogICAgZ2VvbV9ub2RlX3RleHQoYWVzKGxhYmVsID0gbmFtZSksIGNvbCA9ICJkYXJrYmx1ZSIsIHNpemUgPSA0KSArCiAgICB0aGVtZV9ncmFwaChiYXNlX2ZhbWlseSA9ICJBcmlhbCBOYXJyb3ciKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgIGxhYnModGl0bGUgPSAiQ29vY2N1cnJlbmNlcyB3aXRoaW4gc2VudGVuY2U6IEFGVEVSIiwgc3VidGl0bGUgPSAiTm91bnMgJiBBZGplY3RpdmUiKQogIAp9CgoKQ09fT0Nfbm91bl9hZGpfc2FtZV9zZW50LmJlZm9yZSh4X29kZC5iZWZvcmUpCkNPX09DX25vdW5fYWRqX3NhbWVfc2VudC5hZnRlcih4X2V2ZW4uYWZ0ZXIpCgoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgpDT19PQ19ub3VuX2Fkal9mb2xsb3dpbmcuYmVmb3JlIDwtIGZ1bmN0aW9uKGRmKXsKICBjb29jIDwtIGNvb2NjdXJyZW5jZShkZiRsZW1tYSwgcmVsZXZhbnQgPSBkZiR1cG9zICVpbiUgYygiTk9VTiIsICJBREoiKSwgc2tpcGdyYW0gPSAxKQogIGhlYWQoY29vYykKICAKICB3b3JkbmV0d29yayA8LSBoZWFkKGNvb2MsIDYwKQogIHdvcmRuZXR3b3JrIDwtIGdyYXBoX2Zyb21fZGF0YV9mcmFtZSh3b3JkbmV0d29yaykKICBnZ3JhcGgod29yZG5ldHdvcmssIGxheW91dCA9ICJmciIpICsKICAgIGdlb21fZWRnZV9saW5rKGFlcyh3aWR0aCA9IGNvb2MsIGVkZ2VfYWxwaGEgPSBjb29jKSwgZWRnZV9jb2xvdXIgPSAibGlnaHRncmVlbiIpICsKICAgIGdlb21fbm9kZV90ZXh0KGFlcyhsYWJlbCA9IG5hbWUpLCBjb2wgPSAiZGFya2dyZWVuIiwgc2l6ZSA9IDQpICsKICAgIHRoZW1lX2dyYXBoKGJhc2VfZmFtaWx5ID0gIkFyaWFsIE5hcnJvdyIpICsKICAgIGxhYnModGl0bGUgPSAiV29yZHMgZm9sbG93aW5nIG9uZSBhbm90aGVyOiBCRUZPUkUiLCBzdWJ0aXRsZSA9ICJOb3VucyAmIEFkamVjdGl2ZSIpCn0KCkNPX09DX25vdW5fYWRqX2ZvbGxvd2luZy5hZnRlciA8LSBmdW5jdGlvbihkZil7CiAgY29vYyA8LSBjb29jY3VycmVuY2UoZGYkbGVtbWEsIHJlbGV2YW50ID0gZGYkdXBvcyAlaW4lIGMoIk5PVU4iLCAiQURKIiksIHNraXBncmFtID0gMSkKICBoZWFkKGNvb2MpCiAgCiAgd29yZG5ldHdvcmsgPC0gaGVhZChjb29jLCA2MCkKICB3b3JkbmV0d29yayA8LSBncmFwaF9mcm9tX2RhdGFfZnJhbWUod29yZG5ldHdvcmspCiAgZ2dyYXBoKHdvcmRuZXR3b3JrLCBsYXlvdXQgPSAiZnIiKSArCiAgICBnZW9tX2VkZ2VfbGluayhhZXMod2lkdGggPSBjb29jLCBlZGdlX2FscGhhID0gY29vYyksIGVkZ2VfY29sb3VyID0gInNreWJsdWUiKSArCiAgICBnZW9tX25vZGVfdGV4dChhZXMobGFiZWwgPSBuYW1lKSwgY29sID0gImRhcmtibHVlIiwgc2l6ZSA9IDQpICsKICAgIHRoZW1lX2dyYXBoKGJhc2VfZmFtaWx5ID0gIkFyaWFsIE5hcnJvdyIpICsKICAgIGxhYnModGl0bGUgPSAiV29yZHMgZm9sbG93aW5nIG9uZSBhbm90aGVyOiBBRlRFUiIsIHN1YnRpdGxlID0gIk5vdW5zICYgQWRqZWN0aXZlIikKfQoKCkNPX09DX25vdW5fYWRqX2ZvbGxvd2luZy5iZWZvcmUoeF9vZGQuYmVmb3JlKQpDT19PQ19ub3VuX2Fkal9mb2xsb3dpbmcuYWZ0ZXIoeF9ldmVuLmFmdGVyKQoKYGBgCgojIyBDb29jY3VyZW5jZXMgKHBhcnQgMikKYGBge3J9CgpDb3JycyA8LSBmdW5jdGlvbihkZil7CiAgZGYkaWQgPC0gdW5pcXVlX2lkZW50aWZpZXIoZGYsIGZpZWxkcyA9IGMoInNlbnRlbmNlX2lkIiwgImRvY19pZCIpKQogIGR0bSA8LSBzdWJzZXQoZGYsIHVwb3MgJWluJSBjKCJOT1VOIiwgIkFESiIpKQogIGR0bSA8LSBkb2N1bWVudF90ZXJtX2ZyZXF1ZW5jaWVzKGR0bSwgZG9jdW1lbnQgPSAiaWQiLCB0ZXJtID0gImxlbW1hIikKICBkdG0gPC0gZG9jdW1lbnRfdGVybV9tYXRyaXgoZHRtKQogIGR0bSA8LSBkdG1fcmVtb3ZlX2xvd2ZyZXEoZHRtLCBtaW5mcmVxID0gNSkKICB0ZXJtY29ycmVsYXRpb25zIDwtIGR0bV9jb3IoZHRtKQogIHkgPC0gYXNfY29vY2N1cnJlbmNlKHRlcm1jb3JyZWxhdGlvbnMpCiAgeSA8LSBzdWJzZXQoeSwgdGVybTEgPCB0ZXJtMiAmIGFicyhjb29jKSA+IDAuMikKICB5IDwtIHlbb3JkZXIoYWJzKHkkY29vYyksIGRlY3JlYXNpbmcgPSBUUlVFKSwgXQogIHByaW50KHlbMToyNSxdKQp9CgpgYGAKYGBge3IgY29ycnN9CgpDb3Jycyh4X29kZC5iZWZvcmUpCgpDb3Jycyh4X2V2ZW4uYWZ0ZXIpCgpgYGAKCmBgYHtyIGZpbmFsfQpgYGAKCgoKCgpbaG9tZV0oaHR0cHM6Ly9icmVncmVlbi5naXRodWIuaW8vKQo=